home *** CD-ROM | disk | FTP | other *** search
/ Utilities Professional 1-1500 / Utilities Professional 1-1500 (1994)(WPD)[!].iso / 07511000 / var0821.dms / var0821.adf / SnoopDos / src / snoopdos.c < prev    next >
C/C++ Source or Header  |  1992-09-02  |  41KB  |  1,380 lines

  1. /*
  2.  *        SNOOPDOS.C                                    vi:ts=4
  3.  *
  4.  *        (C) Copyright Eddy Carroll, December 1992. Freely distributable.
  5.  *
  6.  *        SnoopDos patches into dos.library and outputs a message to a
  7.  *        debugging window or file whenever a process calls certain DOS
  8.  *        functions.
  9.  *
  10.  *        Type SnoopDos -h for a list of available options. See the
  11.  *        documentation for further details.
  12.  *
  13.  *        Compiles under SAS/C V6.1. Tabs are set every 4 columns.
  14.  *
  15.  *        Revisions
  16.  *        ---------
  17.  *        30 Oct 90:  Fixed potential deadlock problem in termination code
  18.  *                    Improved handling of NULL lock (i.e. boot device)
  19.  *
  20.  *        27 Jan 91:    Really fixed deadlock problem in termination code!
  21.  *
  22.  *       8 Feb 91:  Fixed small bug in tiny.a. Saved 2K by replacing
  23.  *                  sprintf in lc.lib with a version that uses RawDoFmt.
  24.  *
  25.  *        21 Oct 91:    Fixed silly bug with -z option whereby if a space was
  26.  *                    left between the z and the filename, SnoopDos would
  27.  *                    crash the system. I had mistyped extfilename as extfile.
  28.  *                    Silly silly silly!
  29.  *
  30.  *         7 Jul 92:    Fixed bug reported by Matthias Scheler, whereby
  31.  *                    DeleteFile() sometimes trashed the return value.
  32.  *                    This was a simple goof-up on my part (I forgot to
  33.  *                    return the result from the function!) Also made exit
  34.  *                    handling more robust to avoid lockups on exit, and
  35.  *                    added -i option to ignore Shell Lock() commands and
  36.  *                    Shell & Workbench CurrentDir() commands. 
  37.  *
  38.  *         19 Aug 92: Fixed bug introduced in V1.5 with -i whereby many more
  39.  *                    programs than just the CLI/Shell would have their Lock()
  40.  *                    and CurrentDir() calls ignored if -i was enabled.
  41.  *
  42.  *         17 Dec 92: Some new features. -zd for serial port output, -z+
  43.  *                    option for appending output to an existing log file.
  44.  *                    File output now buffered; -n disabled this. Fixed bug
  45.  *                    where current directory wasn't inherited by background
  46.  *                    process. Fixed version string. CON: window now autosizes
  47.  *                    depending on the user's default system font. Added -s
  48.  *                    option to include CR's at the end of every output line.
  49.  */
  50.  
  51. #include "system.h"
  52.  
  53. #define tolower(ch)    (((ch) >= 'A' && (ch) <= 'Z') ? (ch) + 0x20 : (ch))
  54.  
  55. /*
  56.  *        Assorted strings
  57.  */
  58.  
  59. #define NAME    "SnoopDos 1.7"
  60.  
  61. char *Version = "$VER: " NAME " (17.12.92)";
  62.  
  63. #define TITLE \
  64. NAME ". Copyright \251 Eddy Carroll, December 1992. Freely distributable."
  65.  
  66. char *HEADER =
  67. "Process name          Func  Filename                                Mode Res."
  68. "\r\n"
  69. "------------          ----  --------                                ---- ----"
  70. "\r\n";
  71.  
  72. #define PORTNAME        NAME
  73.  
  74. #define FILEBUFSIZE        8192                /* Size of output file buffer    */
  75.  
  76. /*
  77.  *        These defines are for use with the -i1 option; when enabled,
  78.  *        Lock() calls by the tasks called IGNORE_CLI and IGNORE_BG will
  79.  *        be ignored and CurrentDir() calls by all three tasks will also
  80.  *        be ignored. Basically, this avoids the frenzy of activity whenever
  81.  *        the Shell searches for an executable or Workbench does its once a
  82.  *        second scan of the mounted volumes.
  83.  */
  84. #define IGNORE_CLI        "Shell Process"
  85. #define IGNORE_BG        "Background CLI"
  86. #define IGNORE_WB        "Workbench"
  87.  
  88. /*
  89.  *        The following message array contains both colour and non-colour
  90.  *        versions of the various short message strings displayed.
  91.  */
  92. char *msgs[][2] = {
  93. /* Monochrome           Colour   */
  94. /* ----------           ------   */
  95.     "OLD ",                "OLD ",
  96.     "NEW ",                "\033[33mNEW\033[0m ",
  97.     "R/W ",                "\033[32mR/W\033[0m ",
  98.     "??? ",                "??? ",
  99.     "SHAR",                "SHAR",
  100.     "EXCL",                "\033[33mEXCL\033[0m",
  101.     "????",                "????",
  102.     "Okay\r\n",            "Okay\r\n",
  103.     "Fail\r\n",            "\033[33mFail\033[0m\r\n",
  104.     ">",                "\033[33m>\033[0m",
  105.     "> (Done)",            "> (Done)",
  106.     "Warning: Missed",    "\033[33mWarning:\033[0m Missed",
  107.     ">>>>\r\n",            ">>>>\r\n",
  108.     "Open",                "Open",
  109.     "Lock",                "\033[33mLock\033[0m",
  110.     "Load",                "\033[32mLoad\033[0m",
  111.     "Exec",                "\033[32mExec\033[0m",
  112.     "CD  ",                "CD  ",
  113.     "Del ",                "\033[33mDel\033[0m "
  114. };
  115.  
  116. #define TXT_OLD        msgs[ 0][colour]
  117. #define TXT_NEW        msgs[ 1][colour]
  118. #define TXT_R_W        msgs[ 2][colour]
  119. #define TXT_QM3        msgs[ 3][colour]
  120. #define TXT_SHAR    msgs[ 4][colour]
  121. #define TXT_EXCL    msgs[ 5][colour]
  122. #define TXT_QM4        msgs[ 6][colour]
  123. #define TXT_OKAY    msgs[ 7][colour]
  124. #define TXT_FAIL    msgs[ 8][colour]
  125. #define TXT_POINT    msgs[ 9][colour]
  126. #define TXT_DONE    msgs[10][colour]
  127. #define TXT_WARN    msgs[11][colour]
  128. #define TXT_NEST    msgs[12][colour]
  129. #define TXT_OPEN    msgs[13][colour]
  130. #define TXT_LOCK    msgs[14][colour]
  131. #define TXT_LOAD    msgs[15][colour]
  132. #define TXT_EXEC    msgs[16][colour]
  133. #define TXT_CURDIR    msgs[17][colour]
  134. #define TXT_DELETE    msgs[18][colour]
  135.  
  136. #define POINT(x)    ((x) ? TXT_POINT : " ")
  137.  
  138. /*
  139.  *        Now some standard system-type macros
  140.  */
  141. #define reg_d0    register __d0
  142. #define reg_d1    register __d1
  143. #define reg_d2    register __d2
  144. #define reg_d3    register __d3
  145. #define reg_a0    register __a0
  146.  
  147. #define BTOC(x)    (void *)(((ULONG)x) << 2)
  148.  
  149. #define D_S(name, type) char c_##name[sizeof(type)+3];\
  150.                         type *name = (type *)((long)(c_##name+3) & ~3)
  151.  
  152. extern __asm BPTR CallOpen(reg_d1 UBYTE *, reg_d2 int);
  153. extern __asm BPTR CallLock(reg_d1 UBYTE *, reg_d2 int);
  154. extern __asm BPTR CallLoadSeg(reg_d1 UBYTE *);
  155. extern __asm LONG CallExecute(reg_d1 UBYTE *, reg_d2 BPTR, reg_d3 BPTR);
  156. extern __asm BPTR CallCurrentDir(reg_d1 BPTR);
  157. extern __asm LONG CallDeleteFile(reg_d1 UBYTE *);
  158.  
  159. /*
  160.  *        Structure used to pass messages back and fro
  161.  */
  162. typedef struct {
  163.     struct Message msg;            /* Standard message header    */
  164.     struct Process *process;    /* Sending process id        */
  165.     int msgtype;                /* Message type, see below    */
  166.     int  data1;                    /* Data field 1                */ 
  167.     void *data2;                /* Data field 2                */
  168. } MYMSG;
  169.  
  170. /*
  171.  *        Now the various settings that can be set to affect the monitoring
  172.  */
  173. typedef struct {
  174.     int set_doopen;                /* If true, monitor Open()            */
  175.     int    set_dolock;                /* If true, monitor Lock()            */
  176.     int set_doloadseg;            /* If true, monitor LoadSeg()        */
  177.     int set_doexecute;            /* If true, monitor Execute()        */
  178.     int set_docurdir;            /* If true, monitor CurrentDir()    */
  179.     int set_dodelete;            /* If true, monitor DeleteFile()    */
  180.     int    set_showfullpath;        /* If true, display full paths        */
  181.     int set_sleepwait;            /* If true, sleep if necessary        */
  182.     int set_snoopactive;        /* If true, monitoring is active    */
  183.     int set_colour;                /* If true, use ANSI colour codes    */
  184.     int set_ignoretasks;        /* If true, ignore workbench CD        */
  185.     int set_stripcr;            /* If true, strip CR's from output    */
  186. } SETTINGS;
  187.  
  188. /*
  189.  *        Default settings
  190.  */
  191. SETTINGS settings = { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 };
  192.  
  193. /*
  194.  *        These defines allow the various settings to be accessed as
  195.  *        normal variables rather than structure members; this is purely
  196.  *        for convenience.
  197.  */
  198. #define doopen            settings.set_doopen
  199. #define dolock            settings.set_dolock
  200. #define doloadseg        settings.set_doloadseg
  201. #define doexecute        settings.set_doexecute
  202. #define docurdir        settings.set_docurdir
  203. #define dodelete        settings.set_dodelete
  204. #define showfullpath    settings.set_showfullpath
  205. #define sleepwait        settings.set_sleepwait
  206. #define snoopactive        settings.set_snoopactive
  207. #define colour            settings.set_colour
  208. #define ignoretasks        settings.set_ignoretasks
  209. #define stripcr            settings.set_stripcr
  210.  
  211. /*
  212.  *        Now the various message types that can be sent
  213.  */
  214. typedef enum {
  215.     MSG_QUIT,            /* Quit                     */
  216.     MSG_GETOPTIONS,        /* Read options                */
  217.     MSG_SETOPTIONS,        /* Update options            */
  218.     MSG_OPEN,            /* Open file                */
  219.     MSG_OPEN_DONE,        /* Open file completed        */
  220.     MSG_LOCK,            /* Lock file                */
  221.     MSG_LOCK_DONE,        /* Lock file completed        */
  222.     MSG_LOADSEG,        /* LoadSeg file                */
  223.     MSG_LOADSEG_DONE,    /* LoadSeg file completed    */
  224.     MSG_EXECUTE,        /* Execute command            */
  225.     MSG_CURDIR,            /* CurrentDir                */
  226.     MSG_DELETE,            /* DeleteFile                */
  227.     MSG_DELETE_DONE        /* DeleteFile completed        */
  228. } MSGTYPES;
  229.  
  230. struct GfxBase *GfxBase;        /* Pointer to GfxBase                        */
  231.  
  232. struct SignalSemaphore sem[1];
  233. struct MsgPort   *myport;        /* Pointer to background SnoopDos msg port    */
  234. struct MsgPort   *inport;        /* Pointer to our own message port            */
  235. struct Task      *snooptask;    /* Pointer to our own task                    */
  236. BPTR debugwin;                    /* Output file or window                    */
  237. BPTR stderr;                    /* Standard CLI console                        */
  238. BPTR curdir;                    /* Lock on SnoopDos's current directory     */
  239. int  serialio;                    /* True if output directed to serial port    */
  240. int  appendmode;                /* True if opening file for append mode        */
  241. int  nobuffer;                    /* True if we are not to buffer file output    */
  242. int  buffering;                    /* True if currently buffering file output    */
  243. int  batchmode;                    /* True if printing to non-interactive file    */
  244. int  colsel;                    /* True if colour option was specified        */
  245. char logfilename[150];            /* Name of window/file to open if specified    */
  246. char outbuf[800];                /* Output buffer used by myprintf() etc.    */
  247. char *filebuffer;                /* Pointer to possible output file buffer    */
  248. int  filebufpos;                /* Current offset into file buffer            */
  249.  
  250. int missed_open_sig;            /* Signal to indicate Open() missed            */
  251. int missed_lock_sig;            /* Signal to indicate Lock() missed            */
  252. int missed_loadseg_sig;            /* Signal to indicate LoadSeg() missed        */
  253. int missed_execute_sig;            /* Signal to indicate Execute() missed        */
  254. int missed_curdir_sig;            /* Signal to indicate CurrentDir() missed    */
  255. int missed_delete_sig;            /* Signal to indicate DeleteFile() missed    */
  256. int portsig;                    /* Signal used by our public port            */
  257.  
  258. /**************************** Start of Functions ****************************/
  259.  
  260. /*
  261.  *        cleanup()
  262.  *        ---------
  263.  *        Cleans up all active resources and exits. If err is -1, then
  264.  *        returns instead of exiting.
  265.  */
  266. void cleanup(int err)
  267. {
  268.     static int called = 0;
  269.  
  270.     if (called++)        /* Make sure not called twice by accident */
  271.         return;
  272.  
  273.     if (filebuffer)    FreeMem(filebuffer, FILEBUFSIZE);
  274.     if (inport)        DeletePort(inport);
  275.     if (debugwin)    Close(debugwin);
  276.     if (stderr)        Close(stderr);
  277.  
  278.     if (err != -1)
  279.         exit(err);
  280. }
  281.  
  282. /*
  283.  *        myprintf(), myfprintf()
  284.  *        -----------------------
  285.  *        Two low cost alternatives to printf that go directly to AmigaDOS
  286.  *        Note we deliberately use the pre-ANSI declaration, to avoid Lattice
  287.  *        kicking up a fuss about the wrong number of parameters.
  288.  */
  289. void myprintf(format, p1, p2, p3, p4, p5, p6)
  290. UBYTE *format;
  291. ULONG p1, p2, p3, p4, p5, p6;
  292. {
  293.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  294.     if (stripcr) {
  295.         char *p = outbuf, *q = outbuf;
  296.  
  297.         while (*p) {
  298.             if (*p != '\r')
  299.                 *q++ = *p;
  300.             p++;
  301.         }
  302.         *q = '\0';
  303.     }
  304.     Write(Output(), outbuf, strlen(outbuf));
  305. }
  306.  
  307. /*
  308.  *        Output a formatted string to a disk file. If buffering is
  309.  *        true, then the output is buffered internally and only flushed
  310.  *        when full. Making the format string NULL causes the current
  311.  *        buffer to be flushed.
  312.  */
  313. void myfprintf(file, format, p1, p2, p3, p4, p5, p6)
  314. BPTR file;
  315. char *format;
  316. ULONG p1, p2, p3, p4, p5, p6;
  317. {
  318.     if (format == NULL) {
  319.         if (buffering && filebufpos) {
  320.             Write(file, filebuffer, filebufpos);
  321.             filebufpos = 0;
  322.         }
  323.         return;
  324.     }
  325.     sprintf(outbuf, format, p1, p2, p3, p4, p5, p6);
  326.     if (stripcr) {
  327.         char *p = outbuf, *q = outbuf;
  328.  
  329.         while (*p) {
  330.             if (*p != '\r')
  331.                 *q++ = *p;
  332.             p++;
  333.         }
  334.         *q = '\0';
  335.     }
  336.     if (file) {
  337.         int len = strlen(outbuf);
  338.  
  339.         if (buffering) {
  340.             if ((filebufpos + len) > FILEBUFSIZE) {
  341.                 Write(file, filebuffer, filebufpos);
  342.                 filebufpos = 0;
  343.             }
  344.             memcpy(filebuffer + filebufpos, outbuf, len);
  345.             filebufpos += len;
  346.         } else {
  347.             Write(file, outbuf, strlen(outbuf));
  348.         }
  349.     } else {            /* Serial debugging output */
  350.         KPutStr(outbuf);
  351.     }
  352. }
  353.  
  354. /*
  355.  *        sendmsg()
  356.  *        ---------
  357.  *        Sends a message to myport, and waits for a reply to arrive.
  358.  *        A message type and some message data are all that need be provided
  359.  *        by the caller; the callee will provide the rest. Doesn't return
  360.  *        until a reply has been received.
  361.  */
  362. void sendmsg(int type, int data1, void *data2)
  363. {
  364.     MYMSG msg;
  365.     struct Process *me = (struct Process *)FindTask(0);
  366.     struct MsgPort *replyport = &me->pr_MsgPort;
  367.  
  368.     msg.msg.mn_Node.ln_Type = NT_MESSAGE;
  369.     msg.msg.mn_Length       = sizeof(MYMSG);
  370.     msg.msg.mn_ReplyPort    = replyport;
  371.     msg.process                = me;
  372.     msg.msgtype             = type;
  373.     msg.data1               = data1;
  374.     msg.data2               = data2;
  375.  
  376.     PutMsg(myport, &msg);
  377.     WaitPort(replyport);
  378.     GetMsg(replyport);
  379. }
  380.  
  381.  
  382. /*
  383.  *        getlockpath()
  384.  *        -------------
  385.  *        Returns a pointer to a string containing the full path for the
  386.  *        specified lock. You must copy the string before calling this
  387.  *        routine again.
  388.  */
  389. char *getlockpath(BPTR lock)
  390. {
  391.     struct Process *p = (struct Process *)FindTask(0L);
  392.     APTR oldwin = p->pr_WindowPtr;
  393.     static char path[300];
  394.     int pos = 299;
  395.     BPTR mylock;
  396.     char *name;
  397.     D_S(fib, struct FileInfoBlock);
  398.     int err = 0;
  399.  
  400.     p->pr_WindowPtr = (APTR)-1;    /* Disable error requesters */
  401.     mylock = DupLock(lock);
  402.  
  403.     path[pos] = '\0';
  404.  
  405.     do {
  406.         int len;
  407.         BPTR newlock;
  408.  
  409.         if (!Examine(mylock, fib)) {
  410.             UnLock(mylock);
  411.             err++;
  412.             break;
  413.         }
  414.         name = fib->fib_FileName;
  415.         if (*name == '\0')
  416.             name = "RAM";        /* Workaround for old RAM: disk bug */
  417.         len = strlen(name);
  418.         pos = pos - len - 1;
  419.         newlock = ParentDir(mylock);
  420.         UnLock(mylock);
  421.         mylock = newlock;
  422.         strncpy(path + pos, name, len);
  423.         if (mylock)
  424.             path[pos + len] = '/';
  425.         else
  426.             path[pos + len] = ':';
  427.     } while (mylock);
  428.     p->pr_WindowPtr = oldwin;    /* Enable error requesters again */
  429.  
  430.     if (err) {
  431.         /*
  432.          *        Volume not present so have to be happy with just
  433.          *        returning the volume node instead.
  434.          */
  435.         struct FileLock *fl;
  436.         struct DeviceList *dl;
  437.         UBYTE *name;
  438.  
  439.         if (lock) {
  440.             fl = BTOC(lock);
  441.             dl = BTOC(fl->fl_Volume);
  442.             name = BTOC(dl->dl_Name);
  443.  
  444.             strncpy(path, name + 1, *name);
  445.             path[*name] = '\0';
  446.         } else {
  447.             strcpy(path, "<Boot device>");
  448.         }
  449.         strcat(path, ":.../");
  450.         return (path);
  451.     } else
  452.         return (path + pos);
  453. }
  454.  
  455. /*
  456.  *        makepath()
  457.  *        ----------
  458.  *        Builds a full path string given a process ptr and a filename.
  459.  *        If the filename includes relative references (// or :)
  460.  *        these are handled also. The point of this is to resolve relative
  461.  *        directory references.
  462.  *
  463.  *        Returns TRUE if a new string was generated, FALSE if the existing
  464.  *        string didn't depend on the current directory (this doesn't affect
  465.  *        the output stored in buf though).
  466.  */
  467. int makepath(char *buf, BPTR curdir, char *filename)
  468. {
  469.     char *origfilename = filename;
  470.     int pos;
  471.     int doneroot = 0;
  472.  
  473.     /*
  474.      *        Special check for the 'current process console' file '*'
  475.      */
  476.     if (strcmp(filename, "*") == 0) {
  477.         strcpy(buf, filename);
  478.         return (FALSE);
  479.     }
  480.  
  481.     strcpy(buf, getlockpath(curdir));
  482.     pos = strlen(buf);
  483.  
  484.     for (;;) {
  485.         if (!doneroot && *filename == ':') {
  486.             /*
  487.              *        Path is relative to root
  488.              */
  489.             doneroot = 1;
  490.             for (pos = 0; buf[pos] && buf[pos] != ':'; pos++)
  491.                 ;
  492.             if (buf[pos] == ':')
  493.                 pos++;
  494.         } else if (*filename == '/') {
  495.             /*
  496.              *        Path is relative to parent directory; if none, then
  497.              *        remains the same.
  498.              */
  499.             int newpos = pos - 2;
  500.             while (newpos >= 0 && buf[newpos] != '/' && buf[newpos] != ':')
  501.                 newpos--;
  502.             if (newpos >= 0)
  503.                 pos = newpos + 1;
  504.         } else {
  505.             /*
  506.              *        No more special characters; just append what's left of
  507.              *        the filename.
  508.              */
  509.             if (!doneroot) {
  510.                 /*
  511.                  *        If the filename wasn't relative to the root
  512.                  *        directory, then make sure there are no device/volume
  513.                  *        references contained within it. We copy the original
  514.                  *        filename rather than the currently modified version
  515.                  *        since a volume name of /A: is legal and the / would
  516.                  *        be stripped off the modified name.
  517.                  */
  518.                 char *p;
  519.                 for (p = filename; *p; p++) {
  520.                     if (*p == ':') {
  521.                         strcpy(buf, origfilename);
  522.                         return (0);
  523.                     }
  524.                 }
  525.             }
  526.             strcpy(buf + pos, filename);
  527.             return (1);
  528.         }
  529.         filename++;
  530.     }
  531. }
  532.  
  533. /*
  534.  *        mainloop()
  535.  *        ----------
  536.  *        This is the main event loop for SnoopDOS, where everything happens.
  537.  *        Control is passed here when SnoopDOS first starts up. When this
  538.  *        function returns, it terminates the background process.
  539.  */
  540. void mainloop(void)
  541. {
  542.     static char fullname[300];
  543.     BPTR olddir;                    /* Saved current directory                */
  544.     int done   = 0;                    /* True if finished processing            */
  545.     int col0   = 1;                    /* True if cursor in column 0            */
  546.     int nested = 0;                    /* True if nested DOS calls                */
  547.  
  548. #define MISSED_NONE        0            /* Haven't missed anything    */
  549. #define MISSED_OPEN        (1 << 0)    /* Missed Open() call        */
  550. #define MISSED_LOCK        (1 << 1)    /* Missed Lock() call        */
  551. #define MISSED_LOADSEG    (1 << 2)    /* Missed LoadSeg() call    */
  552. #define MISSED_EXECUTE    (1 << 3)    /* Missed Execute() call    */
  553. #define MISSED_CURDIR    (1 << 4)    /* Missed CurrentDir() call    */
  554. #define MISSED_DELETE    (1 << 5)    /* Missed DeleteFile() call    */
  555.  
  556.     int missed = MISSED_NONE;        /* Was a DOS function missed? See above    */
  557.  
  558.     inport = CreatePort(PORTNAME, 0);
  559.     if (!inport) {
  560.         myfprintf(stderr, "SnoopDos: Can't allocate message port.\n");
  561.         cleanup(-1);
  562.         return;
  563.     }
  564.  
  565.     myport  = inport;
  566.     portsig = 1 << myport->mp_SigBit;
  567.  
  568.     /*
  569.      *        Setup a default window string if none has been specified
  570.      */
  571.     if (!*logfilename) {
  572.         int width = 8, height = 8;    /* Default character size is 8 * 8 */
  573.         GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);
  574.         if (GfxBase) {
  575.             width  = GfxBase->DefaultFont->tf_XSize;
  576.             height = GfxBase->DefaultFont->tf_YSize;
  577.             CloseLibrary(GfxBase);
  578.         }
  579.         sprintf(logfilename, "CON:0/0/%ld/%ld/%s",
  580.                 width * 77 + 24, height * 14 + 15, TITLE);
  581.     }
  582.         
  583.     /*
  584.      *        Allocate signals
  585.      */
  586.     missed_open_sig       = 1 << AllocSignal(-1);
  587.     missed_lock_sig       = 1 << AllocSignal(-1);
  588.     missed_loadseg_sig = 1 << AllocSignal(-1);
  589.     missed_execute_sig = 1 << AllocSignal(-1);
  590.     missed_curdir_sig  = 1 << AllocSignal(-1);
  591.     missed_delete_sig  = 1 << AllocSignal(-1);
  592.  
  593.     if (    missed_open_sig    == -1  || missed_lock_sig    == -1  ||
  594.             missed_loadseg_sig == -1  || missed_execute_sig == -1  ||
  595.             missed_curdir_sig  == -1  || missed_delete_sig  == -1) {
  596.         myfprintf(stderr, "SnoopDos: Can't allocate enough signals.\n");
  597.         cleanup(-1);
  598.         return;
  599.     }
  600.  
  601.      /*
  602.       *        Setup output file, either serial port debugging, a standard
  603.       *        CON: or AUX: file, or a non-interactive logfile.
  604.       */
  605.     olddir = CurrentDir(curdir);/* Switch to directory we were run from */
  606.     if (serialio) {
  607.         debugwin  = NULL;        /* Allows KPutStr() to be used instead    */
  608.         batchmode = 1;            /* Enable batch mode messages            */
  609.     } else {
  610.         if (!appendmode)
  611.             debugwin = Open(logfilename, MODE_NEWFILE);
  612.         else {
  613.             debugwin = Open(logfilename, MODE_READWRITE);
  614.             if (debugwin)
  615.                 Seek(debugwin, 0, OFFSET_END);
  616.         }
  617.         if (!debugwin) {
  618.             myfprintf(stderr, "SnoopDos: Can't open log file %s.\n",
  619.                                logfilename);
  620.             cleanup(-1);
  621.             return;
  622.         }
  623.         if (!IsInteractive(debugwin)) {
  624.             batchmode = 1;
  625.             if (!nobuffer) {
  626.                 filebuffer = AllocMem(FILEBUFSIZE, 0);
  627.                 if (filebuffer)
  628.                     buffering = 1;
  629.             }
  630.         }
  631.     }
  632.     CurrentDir(olddir);
  633.     UnLock(curdir); curdir = NULL;    /* Free up directory lock */
  634.     Close(stderr);  stderr = NULL;
  635.  
  636.     if (batchmode) {
  637.         myfprintf(debugwin, "\r\n" TITLE "\r\n\r\n");
  638.         if (colsel == 0) /* Disable ANSI colour unless specifically enabled */
  639.             colour = 0;
  640.     }
  641.     if (!batchmode) {
  642.         myfprintf(debugwin, "\r\n"
  643.         "Type CTRL-E/CTRL-D to enable/disable snooping. Type CTRL-C to exit."
  644.         "\r\n\r\n");
  645.     }
  646.     myfprintf(debugwin, HEADER);
  647.  
  648.     InitSemaphore(sem);
  649.     snooptask = FindTask(0L);
  650.     installdospatch();
  651.  
  652.     /*
  653.      *        Now just sit processing messages
  654.      */
  655.     while (!done) {
  656.         int mask;
  657.  
  658. #define SIGMASK    (portsig | missed_open_sig | missed_lock_sig | \
  659.                  missed_loadseg_sig | missed_execute_sig |     \
  660.                  missed_curdir_sig | missed_delete_sig |       \
  661.                  SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E)
  662.  
  663.         mask = Wait(SIGMASK);
  664.  
  665.         if (mask & SIGBREAKF_CTRL_C)
  666.             done = 1;
  667.  
  668.         if (mask & SIGBREAKF_CTRL_D) {
  669.             if (snoopactive) {
  670.                 myfprintf(debugwin,
  671. "\nSnooping now disabled; type CTRL-E to enable it again.\r\n\r\n");
  672.                 snoopactive = 0;
  673.             }
  674.         }
  675.  
  676.         if (mask & SIGBREAKF_CTRL_E) {
  677.             if (!snoopactive) {
  678.                 myfprintf(debugwin,
  679. "Snooping now enabled; type CTRL-D to disable it again.\r\n\r\n%s", HEADER);
  680.                 snoopactive = 1;
  681.             }
  682.         }
  683.  
  684.         if (mask & missed_open_sig)
  685.             missed |= MISSED_OPEN;
  686.  
  687.         if (mask & missed_lock_sig)
  688.             missed |= MISSED_LOCK;
  689.  
  690.         if (mask & missed_loadseg_sig)
  691.             missed |= MISSED_LOADSEG;
  692.  
  693.         if (mask & missed_execute_sig)
  694.             missed |= MISSED_EXECUTE;
  695.  
  696.         if (mask & missed_curdir_sig)
  697.             missed |= MISSED_CURDIR;
  698.  
  699.         if (mask & missed_delete_sig)
  700.             missed |= MISSED_DELETE;
  701.  
  702.         if (mask & portsig) {
  703.             MYMSG *msg;
  704.  
  705.             /*
  706.              *        Note in the following, there is some slightly tricky
  707.              *        stuff to handle the case where a DOS function calls
  708.              *        another DOS function internally. Under 1.3, this doesn't
  709.              *        happen, but it may under WB 2.0 and in addition, Jim
  710.              *        Goodnow's handy Rez utility patches LoadSeg() so that
  711.              *        it calls Open(). `nested' is true when a nested call
  712.              *        like this happens, and in this case, process names are
  713.              *        printed out prefixed by `> ' to indicate the nesting.
  714.              *
  715.              *        The nesting is detected because an incoming Open/Lock/
  716.              *        LoadSeg message will arrive when the cursor is positioned
  717.              *        to print a Fail/Okay result from a previous call (i.e.
  718.              *        col0 is false; the cursor is not in column 0). When a
  719.              *        result arrives in and the cursor IS in column 0, then
  720.              *        this must be the result from an earlier call so the
  721.              *        nesting is ended. Note that the semaphores used ensures
  722.              *        that this situation can only ever occur as a result of
  723.              *        nesting; other tasks will always wait until they are
  724.              *        properly in sync before sending messages to the main
  725.              *        Snoopdos process.
  726.              *
  727.              *        The more alert among you are probably saying something
  728.              *        like "Ah, but what if SnoopDos is configured to display
  729.              *        all DOS calls, even if it means forcing some tasks to
  730.              *        sleep until SnoopDos can process them? Then, when a
  731.              *        task calls LoadSeg() (say) and LoadSeg() calls Open(),
  732.              *        Open() will sleep forever waiting for the semaphore
  733.              *        Obtain()ed by LoadSeg() to become free again." Well,
  734.              *        in fact, it won't, since exec's ObtainSemaphore() call
  735.              *        will suceed even when the semaphore is in use, IF the
  736.              *        caller is the task that owns the semaphore -- and in such
  737.              *        a case, the caller will be the same task. (It took me a
  738.              *        while to figure out exactly how things were working --
  739.              *        it looked like I should get a serious lockup in certain
  740.              *        circumstances).
  741.              *        
  742.              */
  743.             while ((msg = (MYMSG *)GetMsg(myport)) != NULL) {
  744.                 /*
  745.                  *        Get the name of the process/command for
  746.                  *        printing out if necessary.
  747.                  */
  748.                 static char namebuf[256];
  749.                 UBYTE *procname = msg->process->pr_Task.tc_Node.ln_Name;
  750.                 struct CommandLineInterface *cli = BTOC(msg->process->pr_CLI);
  751.                 BPTR curdir = msg->process->pr_CurrentDir;
  752.                 UBYTE *s;
  753.                 UBYTE *filename;
  754.                 int newname;    /* If true, a new name was generated */
  755.  
  756.                 if (!col0 && (msg->msgtype == MSG_OPEN        ||
  757.                               msg->msgtype == MSG_LOCK        ||
  758.                               msg->msgtype == MSG_LOADSEG    ||
  759.                               msg->msgtype == MSG_CURDIR    ||
  760.                               msg->msgtype == MSG_DELETE)) {
  761.                     nested = 1;
  762.                     myfprintf(debugwin, TXT_NEST);
  763.                 }
  764.                 if (cli && cli->cli_Module) {
  765.                     UBYTE *cliname = BTOC(cli->cli_CommandName);
  766.                     if (*cliname > 0) {
  767.                         /* Only use CLI name if it's not null */
  768.                         strncpy(namebuf+2, cliname + 1, *cliname);
  769.                         namebuf[*cliname+2] = '\0';
  770.                         procname = namebuf + 2;
  771.                         if (nested) {
  772.                             /*
  773.                              *        If we're not at column 0, then we're
  774.                              *        calling this DOS function from within
  775.                              *        another DOS function so indent display
  776.                              */
  777.                             procname = namebuf;
  778.                             procname[0] = '>';
  779.                             procname[1] = ' ';
  780.                         }
  781.                     }
  782.                 } else {
  783.                     if (nested) {
  784.                         sprintf(namebuf, "> %s", procname);
  785.                         procname = namebuf;
  786.                     }
  787.                 }
  788.  
  789.                 /*
  790.                  *        Now handle the message
  791.                  */
  792.                 filename = msg->data2;    /* Standard file name            */
  793.                 newname  = 0;            /* No problems expanding it        */
  794.  
  795.                 switch (msg->msgtype) {
  796.  
  797.                     case MSG_QUIT:
  798.                         done = 1;
  799.                         break;
  800.  
  801.                     case MSG_GETOPTIONS:
  802.                         memcpy(msg->data2, &settings, sizeof(SETTINGS));
  803.                         break;
  804.  
  805.                     case MSG_SETOPTIONS:
  806.                         memcpy(&settings, msg->data2, sizeof(SETTINGS));
  807.                         break;
  808.  
  809.                     case MSG_OPEN:
  810.                         if (msg->data1 == MODE_OLDFILE)
  811.                             s = TXT_OLD;
  812.                         else if (msg->data1 == MODE_NEWFILE)
  813.                             s = TXT_NEW;
  814.                         else if (msg->data1 == MODE_READWRITE)
  815.                             s = TXT_R_W;
  816.                         else
  817.                             s = TXT_QM3;
  818.  
  819.                         if (showfullpath) {
  820.                             newname = makepath(fullname, curdir, msg->data2);
  821.                             filename = fullname;
  822.                         }
  823.                         myfprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  824.                                     TXT_OPEN, POINT(newname), filename, s);
  825.                         col0 = 0;
  826.                         break;  
  827.  
  828.                     case MSG_LOCK:
  829.                         if (msg->data1 == ACCESS_READ)
  830.                             s = TXT_SHAR;
  831.                         else if (msg->data1 == ACCESS_WRITE)
  832.                             s = TXT_EXCL;
  833.                         else
  834.                             s = TXT_QM4;
  835.  
  836.                         if (showfullpath) {
  837.                             newname = makepath(fullname, curdir, msg->data2);
  838.                             filename = fullname;
  839.                         }
  840.                         myfprintf(debugwin, "%-21s %s %s%-39s %s ", procname,
  841.                                     TXT_LOCK, POINT(newname), filename, s);
  842.                         col0 = 0;
  843.                         break;
  844.  
  845.                     case MSG_LOADSEG:
  846.                         if (showfullpath) {
  847.                             newname = makepath(fullname, curdir, msg->data2);
  848.                             filename = fullname;
  849.                         }
  850.                         myfprintf(debugwin, "%-21s %s %s%-44s ", procname,
  851.                                     TXT_LOAD, POINT(newname), filename);
  852.                         col0 = 0;
  853.                         break;
  854.  
  855.                     case MSG_EXECUTE:
  856.                         myfprintf(debugwin, "%-21s %s  %s\r\n", procname,
  857.                                                     TXT_EXEC, filename);
  858.                         col0 = 1;
  859.                         break;
  860.  
  861.                     case MSG_CURDIR:
  862.                         {
  863.                             char *dirname = getlockpath(msg->data1);
  864.                             int i = strlen(dirname);
  865.  
  866.                             if (dirname[i-1] == '/')
  867.                                 dirname[i-1] = '\0';
  868.                             myfprintf(debugwin, "%-21s %s  %s\r\n", procname,
  869.                                                     TXT_CURDIR, dirname);
  870.                             col0 = 1;
  871.                         }
  872.                         break;
  873.  
  874.                     case MSG_DELETE:
  875.                         if (showfullpath) {
  876.                             newname = makepath(fullname, curdir, msg->data2);
  877.                             filename = fullname;
  878.                         }
  879.                         myfprintf(debugwin, "%-21s %s %s%-44s ", procname,
  880.                                     TXT_DELETE, POINT(newname), filename);
  881.                         col0 = 0;
  882.                         break;
  883.  
  884.                     case MSG_LOCK_DONE:
  885.                     case MSG_OPEN_DONE:
  886.                     case MSG_LOADSEG_DONE:
  887.                     case MSG_DELETE_DONE:
  888.                         if (col0 && nested) {
  889.                             myfprintf(debugwin, "%-73s", TXT_DONE);
  890.                             nested = 0;
  891.                         }
  892.                         if (msg->data1)
  893.                             myfprintf(debugwin, TXT_OKAY);
  894.                         else
  895.                             myfprintf(debugwin, TXT_FAIL);
  896.                         col0 = 1;
  897.                         break;
  898.                 }
  899.                 ReplyMsg(msg);
  900.             }
  901.         }
  902.  
  903.         /*
  904.          *        Finally, check if we missed a DOS function. If we did,
  905.          *        AND we are in column 0, print out an appropriate message.
  906.          *        Otherwise, wait until next time. This stops the display
  907.          *        getting messed up when waiting to print a 'Fail' or 'Okay'.
  908.          */
  909.         if (missed) {
  910.             if (col0) {
  911.                 if (missed & MISSED_OPEN)
  912.                     myfprintf(debugwin, "%s an Open()\r\n", TXT_WARN);
  913.                 if (missed & MISSED_LOCK)
  914.                     myfprintf(debugwin, "%s a Lock()\r\n", TXT_WARN);
  915.                 if (missed & MISSED_LOADSEG)
  916.                     myfprintf(debugwin, "%s a LoadSeg()\r\n", TXT_WARN);
  917.                 if (missed & MISSED_EXECUTE)
  918.                     myfprintf(debugwin, "%s an Execute()\r\n", TXT_WARN);
  919.                 if (missed & MISSED_CURDIR)
  920.                     myfprintf(debugwin, "%s a CurrentDir()\r\n", TXT_WARN);
  921.                 if (missed & MISSED_DELETE)
  922.                     myfprintf(debugwin, "%s a DeleteFile()\r\n", TXT_WARN);
  923.                 missed = MISSED_NONE;
  924.             }
  925.         }
  926.     }
  927.  
  928.     /*
  929.      *        Remove the port from the public ports list; this stops any
  930.      *        SnoopDos processes started up elsewhere from trying to
  931.      *        communicate with us (which may happen if this process can't
  932.      *        exit immediately).
  933.      */
  934.     RemPort(myport);
  935.     myport->mp_Node.ln_Name = NULL;
  936.     snoopactive = 0;    /* Make sure our patch stops sending msgs to us! */
  937.  
  938.     /*
  939.      *        Now try and obtain our semaphore. Until we can achieve it,
  940.      *        we may still be receiving messages from other tasks telling
  941.      *        us about DOS operations, so, we reply to these as they arrive
  942.      *        (else deadlock occurs).
  943.      */
  944.     while (!AttemptSemaphore(sem)) {
  945.         MYMSG *msg = (MYMSG *)GetMsg(myport);
  946.  
  947.         if (msg)
  948.             ReplyMsg(msg);
  949.         Delay(5);                /* Wait for 0.1 seconds */
  950.     }
  951.     myfprintf(debugwin, "\r\nSnoopDos terminated.\r\n");
  952.     myfprintf(debugwin, NULL);        /* Flush possible output */
  953.  
  954.     /*
  955.      *        Now remove the dospatch, and cleanup
  956.      */
  957.     if (!uninstalldospatch()) {
  958.         /*
  959.          *        Someone else has patched DOSbase so print a message for
  960.          *        the user and keep trying every 5 seconds until we succeed.
  961.          */
  962.         int count = 0;
  963.  
  964.         myfprintf(debugwin,
  965. "\r\n"
  966. "Someone else has patched dos.library so I'll have to wait until they quit."
  967. "\r\n");
  968.         if (!batchmode) {
  969.             myfprintf(debugwin, 
  970.   "This window will close shortly though to stop it from annoying you.\r\n");
  971.         }
  972.         do {
  973.             Delay(50);                    /* Wait for one second */
  974.             if (debugwin) {
  975.                 count++;
  976.                 if (count > 10) {
  977.                     Close(debugwin);
  978.                     debugwin = NULL;
  979.                 }
  980.             }
  981.         } while (!uninstalldospatch());
  982.     }
  983.  
  984.     /*
  985.      *        We do a final wait for 2 seconds, just in case there are any
  986.      *        other tasks still running in our SetFunction'd code. We close
  987.      *        the window/file first, to avoid annoying the user.
  988.      *        (This isn't 100% bulletproof, but it's fairly safe).
  989.      */
  990.     if (debugwin) {
  991.         Close(debugwin);
  992.         debugwin = NULL;    /* Make sure cleanup code doesn't close it too */
  993.     }
  994.     Delay(50);
  995.     /*
  996.      *        Now make one final check to ensure there are no outstanding
  997.      *        messages waiting to be replied too. Note that all this delaying
  998.      *        etc. isn't really a problem since we are now "invisible" to
  999.      *        the rest of the system, having deleted our AmigaDOS patch and
  1000.      *        public port earlier on. It's just a bit of extra bullet
  1001.      *        proofing to reduce the risk of race conditions during exit.
  1002.      */
  1003.     if (myport) {
  1004.         MYMSG *msg;
  1005.         while ((msg = (MYMSG *)GetMsg(myport)) != NULL)
  1006.             ReplyMsg(msg);
  1007.     }
  1008.     Delay(50*2);    /* And give such code 2 seconds to complete */
  1009.     cleanup(-1);
  1010. }
  1011.  
  1012. /*
  1013.  *        main()
  1014.  *        ------
  1015.  *        Mainline. Parses the command line and, if necessary, kicks off the
  1016.  *        background task to do the real work.
  1017.  */
  1018. void __stdargs main(int argc, char **argv)
  1019. {
  1020.     int error  = 0;            /* True if error detected in cmd line    */
  1021.     int quit   = 0;            /* True if we want to quit                */
  1022.  
  1023.     /*
  1024.      *        See if we are already active elsewhere; if yes, then read in
  1025.      *        the default settings used by that process.
  1026.      */
  1027.  
  1028.     myport = FindPort(PORTNAME);
  1029.     if (myport)
  1030.         sendmsg(MSG_GETOPTIONS, 0, &settings);
  1031.  
  1032.     while (argc > 1 && argv[1][0] == '-') {
  1033.         int val;
  1034.  
  1035.         /* Make -X == -x0 and -x == -x1 */
  1036.         if (argv[1][1] >= 'a' && argv[1][2] != '0')
  1037.             val = 1;
  1038.         else
  1039.             val = 0;
  1040.  
  1041.         switch (tolower(argv[1][1])) {
  1042.  
  1043.             case 'a': colour         = val;
  1044.                       colsel        = 1;   break;    /* Display ANSI colour    */
  1045.             case 'c': docurdir        = val; break;    /* Monitor CurrentDir() */
  1046.             case 'd': dodelete        = val; break;    /* Monitor DeleteFile()    */
  1047.             case 'f': showfullpath    = val; break;    /* Show full filepath    */
  1048.             case 'g': doloadseg        = val; break;    /* Monitor LoadSeg()    */
  1049.             case 'h': error            = 1;   break;    /* Just print help msg    */
  1050.             case 'i': ignoretasks   = val; break;    /* Ignore WBench/Shell  */
  1051.             case 'l': dolock        = val; break;    /* Monitor Lock()        */
  1052.             case 'm': snoopactive    = val; break;    /* Actively monitoring    */
  1053.             case 'n': nobuffer      = 1;   break;    /* Disable output buf   */
  1054.             case 'o': doopen        = val; break;    /* Monitor Open()        */
  1055.             case 'q': quit            = 1;   break;    /* Ask SnoopDos to quit */
  1056.             case 'r': error            = 2;   break;    /* Report settings        */
  1057.             case 's': stripcr       = 0;   break;    /* Disable CR stripping    */
  1058.             case 'w': sleepwait        = val; break;    /* Wait when necessary    */
  1059.             case 'x': doexecute        = val; break;    /* Monitor Execute()    */
  1060.             case 'z':
  1061.                     if (strcmp(argv[1], "-zd") == 0) {
  1062.                         serialio = 1;
  1063.                         stripcr  = 0;
  1064.                     } else if (argv[1][2] == '+' && argv[1][3]) {
  1065.                         strcpy(logfilename, &argv[1][3]);
  1066.                         appendmode = 1;
  1067.                     } else if (argv[1][2]) {
  1068.                         strcpy(logfilename, &argv[1][2]);
  1069.                     } else {
  1070.                         argv++;
  1071.                         argc--;
  1072.                         if (argc > 1)
  1073.                             strcpy(logfilename, argv[1]);
  1074.                     }
  1075.                     if (!*logfilename && !serialio) {
  1076.                         myprintf(
  1077.         "-z must be followed by a filename. Type SnoopDos -h for help.\n");
  1078.                         exit(5);
  1079.                     }
  1080.                     break;
  1081.  
  1082.             default:
  1083.                 myprintf("Unrecognised option %s. "
  1084.                          "Type Snoopdos -h for help.\n", argv[1]);
  1085.                 exit(5);
  1086.         }
  1087.         argv++; argc--;
  1088.     }
  1089.  
  1090.     if (argc > 1)
  1091.         error = 1;
  1092.  
  1093.     if (error) {
  1094.         if (error == 1) {
  1095.             myprintf(TITLE "\n\n"
  1096. "SnoopDos monitors calls made to various AmigaDOS functions by all processes\n"
  1097. "on the system. The following options are available. Use -x1 or just -x to\n"
  1098. "enable an option, -x0 or -X to disable that option. Current settings in (-)."
  1099. "\n\n");
  1100.         } else
  1101.             myprintf("Current " NAME " settings:\n\n");
  1102.  
  1103. #define O(x,y) myprintf(x, y ? "(on)" : "(off)")
  1104. O("    -a   Use ANSI colour sequences                     %s\n", colour);
  1105. O("    -c   Monitor CurrentDir() calls                    %s\n", docurdir);
  1106. O("    -d   Monitor DeleteFile() calls                    %s\n", dodelete);
  1107. O("    -f   Display full filename paths                   %s\n", showfullpath);
  1108. O("    -g   Monitor LoadSeg() calls                       %s\n", doloadseg);
  1109. O("    -i   Ignore some Shell & Workbench calls           %s\n", ignoretasks);
  1110. O("    -l   Monitor Lock() calls                          %s\n", dolock);
  1111. O("    -m   Globally enable/disable monitoring            %s\n", snoopactive);
  1112. O("    -o   Monitor Open() calls                          %s\n", doopen);
  1113. O("    -s   Add CR's to end of each output line           %s\n", !stripcr);
  1114. O("    -w   Display all activity, sleeping if necessary   %s\n", sleepwait);
  1115. O("    -x   Monitor Execute() calls                       %s\n", doexecute);
  1116.  
  1117.         if (error == 1) {
  1118.             myprintf("\n"
  1119.   "    -h   Print out this help message\n"
  1120.   "    -n   Don't buffer log file output\n"
  1121.   "    -q   Tell SnoopDos to quit\n"
  1122.   "    -r   Report current SnoopDos settings\n" 
  1123.   "    -z$  Output to file $ (e.g. -zCON:0/0/640/100/SnoopDos or -zlogfile)\n"
  1124.   "    -z+$ Append output to file $\n"
  1125.   "    -zd  Output to debugging terminal on serial port\n"
  1126.               );
  1127.         }
  1128.         cleanup(5);
  1129.     }
  1130.  
  1131.     /*
  1132.      *        First see are we already installed. If so, send a copy of the
  1133.      *        updated settings to the background process, or send a QUIT
  1134.      *        message if the user wanted to quit.
  1135.      */
  1136.     if (myport) {
  1137.         if (*logfilename) {
  1138.             myprintf("You can't use -z when SnoopDos is already running.\n");
  1139.             cleanup(0);
  1140.         }
  1141.         if (quit)
  1142.             sendmsg(MSG_QUIT, 0, 0);
  1143.         else
  1144.             sendmsg(MSG_SETOPTIONS, 0, &settings);
  1145.         cleanup(0);
  1146.     }
  1147.  
  1148.     /*
  1149.      *        If user wanted to quit and we weren't already running, just
  1150.      *        quit without doing anything.
  1151.      */
  1152.     if (quit) {
  1153.         myprintf("There is no background SnoopDos process to tell to quit!\n");
  1154.         cleanup(0);
  1155.     }
  1156.  
  1157.     /*
  1158.      *        Not installed, so install ourselves. First of all though, we
  1159.      *        kick ourselves into the background and return control to the
  1160.      *        calling CLI. We need to do this before we start creating ports
  1161.      *        and opening files, to prevent Exec and AmigaDOS getting
  1162.      *        confused about which task we are.
  1163.      *
  1164.      *        Also open stderr channel for the new task to output fatal
  1165.      *        error messages to; the new task takes responsibility for closing
  1166.      *        this channel.
  1167.      */
  1168.     stderr = Open("*", MODE_NEWFILE);
  1169.     curdir = Lock("", ACCESS_READ);    /* Let sub-process access cur-dir */
  1170.     if (!res("SnoopDos", 5, mainloop, 4000)) {
  1171.         myprintf("Couldn't spawn background SnoopDos task.\n");
  1172.         UnLock(curdir);
  1173.         curdir = NULL;
  1174.         cleanup(10);
  1175.     }
  1176. }
  1177.  
  1178. /*
  1179.  *        New_Open()
  1180.  *        ----------
  1181.  *        This is the new Open() code. It checks to see if the calling task
  1182.  *        is actually the SnoopDos task; if it is, then it essentially does
  1183.  *        nothing (this stops any possible deadlock occurring). Otherwise,
  1184.  *        it sends a message to the task with the pertinent info.
  1185.  *
  1186.  *        If sleeping is disabled then if the semaphore isn't immediately
  1187.  *        available, a signal is sent to the Snoopdos task telling it that
  1188.  *        it's missed trapping a function.
  1189.  */
  1190. __asm BPTR New_Open(reg_d1 char *filename, reg_d2 int mode)
  1191. {
  1192.     BPTR filehandle;
  1193.     geta4();
  1194.     if (snoopactive && doopen && FindTask(0) != snooptask) {
  1195.         if (sleepwait)
  1196.             ObtainSemaphore(sem);
  1197.         else if (!AttemptSemaphore(sem)) {
  1198.             Signal(snooptask, missed_open_sig);
  1199.             return (CallOpen(filename, mode));
  1200.         }
  1201.         sendmsg(MSG_OPEN, mode, filename);
  1202.         filehandle = CallOpen(filename, mode);
  1203.         sendmsg(MSG_OPEN_DONE, filehandle, 0);
  1204.         ReleaseSemaphore(sem);
  1205.         return (filehandle);
  1206.     } else {
  1207.         return (CallOpen(filename, mode));
  1208.     }
  1209. }
  1210. /*
  1211.  *        New_Lock()
  1212.  *        ---------
  1213.  *        Replacement for Lock(), all the comments for NewOpen() apply
  1214.  *        equally here.
  1215.  */
  1216. __asm BPTR New_Lock(reg_d1 char *filename, reg_d2 int mode)
  1217. {
  1218.     BPTR lock;
  1219.     struct Task *task;
  1220.     struct CommandLineInterface *cli;
  1221.     UBYTE *procname;
  1222.  
  1223.     geta4();
  1224.     task     = FindTask(0L);
  1225.     procname = task->tc_Node.ln_Name;
  1226.     cli      = BTOC(((struct Process *)task)->pr_CLI);
  1227.  
  1228.     if (cli && cli->cli_Module) {
  1229.         UBYTE *cliname = BTOC(cli->cli_CommandName);
  1230.         if (*cliname > 0) {
  1231.             /* Only use CLI name if it's not null */
  1232.             procname = cliname + 1;
  1233.         }
  1234.     }
  1235.  
  1236.     if (ignoretasks && (strncmp(procname,IGNORE_CLI,strlen(IGNORE_CLI)) == 0 ||
  1237.                         strncmp(procname,IGNORE_BG, strlen(IGNORE_BG))  == 0))
  1238.         return (CallLock(filename, mode));
  1239.  
  1240.     if (snoopactive && dolock && FindTask(0) != snooptask) {
  1241.         if (sleepwait)
  1242.             ObtainSemaphore(sem);
  1243.         else if (!AttemptSemaphore(sem)) {
  1244.             Signal(snooptask, missed_lock_sig);
  1245.             return (CallLock(filename, mode));
  1246.         }
  1247.         sendmsg(MSG_LOCK, mode, filename);
  1248.         lock = CallLock(filename, mode);
  1249.         sendmsg(MSG_LOCK_DONE, lock, 0);
  1250.         ReleaseSemaphore(sem);
  1251.         return (lock);
  1252.     } else {
  1253.         return (CallLock(filename, mode));
  1254.     }
  1255. }
  1256.  
  1257. /*
  1258.  *        New_LoadSeg()
  1259.  *        -------------
  1260.  *        Replacement for LoadSeg(), all the comments for New_Open() apply
  1261.  *        equally here.
  1262.  */
  1263. __asm BPTR New_LoadSeg(reg_d1 char *filename)
  1264. {
  1265.     BPTR seglist;
  1266.     geta4();
  1267.     if (snoopactive && doloadseg && FindTask(0) != snooptask) {
  1268.         if (sleepwait)
  1269.             ObtainSemaphore(sem);
  1270.         else if (!AttemptSemaphore(sem)) {
  1271.             Signal(snooptask, missed_loadseg_sig);
  1272.             return (CallLoadSeg(filename));
  1273.         }
  1274.         sendmsg(MSG_LOADSEG, 0, filename);
  1275.         seglist = CallLoadSeg(filename);
  1276.         sendmsg(MSG_LOADSEG_DONE, seglist, 0);
  1277.         ReleaseSemaphore(sem);
  1278.         return (seglist);
  1279.     } else {
  1280.         return (CallLoadSeg(filename));
  1281.     }
  1282. }
  1283.  
  1284. /*
  1285.  *        New_Execute()
  1286.  *        ------------
  1287.  *        Replacement for Execute()
  1288.  */
  1289. __asm LONG New_Execute(reg_d1 char *command, reg_d2 BPTR in, reg_d3 BPTR out)
  1290. {
  1291.     geta4();
  1292.     if (snoopactive && doexecute && FindTask(0) != snooptask) {
  1293.         if (sleepwait)
  1294.             ObtainSemaphore(sem);
  1295.         else if (!AttemptSemaphore(sem)) {
  1296.             Signal(snooptask, missed_execute_sig);
  1297.             return (CallExecute(command, in, out));
  1298.         }
  1299.         sendmsg(MSG_EXECUTE, 0, command);
  1300.         ReleaseSemaphore(sem);
  1301.     }
  1302.     return (CallExecute(command, in, out));
  1303. }
  1304.  
  1305. /*
  1306.  *        New_CurrentDir()
  1307.  *        ---------------
  1308.  *        Replacement for CurrentDir()
  1309.  */
  1310. __asm BPTR New_CurrentDir(reg_d1 BPTR newlock)
  1311. {
  1312.     struct Task *task;
  1313.     struct CommandLineInterface *cli;
  1314.     UBYTE *procname;
  1315.  
  1316.     geta4();
  1317.     task     = FindTask(0L);
  1318.     procname = task->tc_Node.ln_Name;
  1319.     cli      = BTOC(((struct Process *)task)->pr_CLI);
  1320.  
  1321.     if (cli && cli->cli_Module) {
  1322.         UBYTE *cliname = BTOC(cli->cli_CommandName);
  1323.         if (*cliname > 0) {
  1324.             /* Only use CLI name if it's not null */
  1325.             procname = cliname + 1;
  1326.         }
  1327.     }
  1328.  
  1329.     /*
  1330.      *        If we are ignoring CD's from the Workbench and Shell tasks
  1331.      *        (to save lots of useless output when running under
  1332.      *        Kickstart 2.0x), then check to see if the task trying to
  1333.      *        do a CD is Workbench, or the shell itself. If it is,
  1334.      *        don't output anything. Note that we distinguish between
  1335.      *        commands running from within the shell, and the shell itself,
  1336.      *        by looking to see if a module has been loaded into the Shell.
  1337.      */
  1338.     if (ignoretasks && (strncmp(procname,IGNORE_WB, strlen(IGNORE_WB))  == 0 ||
  1339.                         strncmp(procname,IGNORE_CLI,strlen(IGNORE_CLI)) == 0 ||
  1340.                         strncmp(procname,IGNORE_BG, strlen(IGNORE_BG))  == 0))
  1341.         return (CallCurrentDir(newlock));
  1342.  
  1343.     if (snoopactive && docurdir && task != snooptask) {
  1344.         if (sleepwait)
  1345.             ObtainSemaphore(sem);
  1346.         else if (!AttemptSemaphore(sem)) {
  1347.             Signal(snooptask, missed_curdir_sig);
  1348.             return (CallCurrentDir(newlock));
  1349.         }
  1350.         sendmsg(MSG_CURDIR, newlock, 0);
  1351.         ReleaseSemaphore(sem);
  1352.     }
  1353.     return (CallCurrentDir(newlock));
  1354. }
  1355.  
  1356. /*
  1357.  *        New_DeleteFile()
  1358.  *        ---------------
  1359.  *        Replacement for DeleteFile()
  1360.  */
  1361. __asm LONG New_DeleteFile(reg_d1 char *filename)
  1362. {
  1363.     LONG success;
  1364.     geta4();
  1365.     if (snoopactive && dodelete && FindTask(0) != snooptask) {
  1366.         if (sleepwait)
  1367.             ObtainSemaphore(sem);
  1368.         else if (!AttemptSemaphore(sem)) {
  1369.             Signal(snooptask, missed_delete_sig);
  1370.             return (CallDeleteFile(filename));
  1371.         }
  1372.         sendmsg(MSG_DELETE, 0, filename);
  1373.         success = CallDeleteFile(filename);
  1374.         sendmsg(MSG_DELETE_DONE, success, 0);
  1375.         ReleaseSemaphore(sem);
  1376.         return (success);
  1377.     } else
  1378.         return (CallDeleteFile(filename));
  1379. }
  1380.